package com.example.jvmti

import android.content.Context
import android.os.Build
import android.os.Bundle
import android.os.Debug
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.example.jvmti.databinding.ActivityMainBinding
import com.example.jvmti.jni.JVMTIAgent
import java.io.File
import java.lang.reflect.Method
import java.nio.file.Files
import java.nio.file.Paths


class MainActivity : AppCompatActivity() {
    companion object{
        private const val TAG = "MainActivity"
        private const val AGENT_LIB_NAME = "native-lib"
    }

    private lateinit var mBinding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(mBinding.root)

        initJVMTI()
        JVMTIAgent.agentInit(getExternalFilesDir(null)?.absolutePath)

        mBinding.btnCreateObject.setOnClickListener {
            Thread{
                for (index in 1 until 100){
                    User("张三$index",index * index)
                }
            }.start()
        }
        mBinding.btnFreeObject.setOnClickListener {
            System.gc()
            System.runFinalization()
        }

    }

    override fun onDestroy() {
        JVMTIAgent.agentRelease()
        super.onDestroy()
    }

    private fun initJVMTI() {
        val path = getLibPath(this.applicationContext) ?: return
        attachAgent(this.applicationContext,path)
    }

    private fun attachAgent(context: Context, path: String) {
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                Debug.attachJvmtiAgent(path, null, context.classLoader)
            }else{
                val vmDebugClazz = Class.forName("dalvik.system.VMDebug")
                val attachAgentMethod: Method =
                    vmDebugClazz.getMethod("attachAgent", String::class.java)
                attachAgentMethod.isAccessible = true
                attachAgentMethod.invoke(null, path)
            }
        }catch (e: Exception){

        }
    }

    private fun getLibPath(context: Context): String? {
        try{
            val classLoader = context.classLoader
            val findLibraryMethod = ClassLoader::class.java.getDeclaredMethod("findLibrary",String::class.java)
            printLog(findLibraryMethod.name)
            val libPath: String? = findLibraryMethod.invoke(classLoader, AGENT_LIB_NAME) as String?
            printLog("so lib path is :$libPath")
            if(libPath == null){
                return null
            }
            //将so拷贝到程序私有目录 /data/data/packageName/files/monitor/agent.so
            val filesDir = context.filesDir
            val jvmtiLibDir = File(filesDir, "monitor")
            if (!jvmtiLibDir.exists()) {
                jvmtiLibDir.mkdirs()
            }
            val agentLibSo = File(jvmtiLibDir, "$AGENT_LIB_NAME.so")
            if (agentLibSo.exists()) {
                agentLibSo.delete()
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                Files.copy(
                    Paths.get(File(libPath).getAbsolutePath()),
                    Paths.get(agentLibSo.getAbsolutePath())
                )
            }
            return agentLibSo.getAbsolutePath()
        }catch (e: Exception){

        }
        return null
    }

    private fun printLog(message: String){
        Log.i(TAG,message)
    }
}